<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>网状粒子效果2</title>
  <style>
    html, body {
      margin: 0;
      padding: 0;
      height: 100%;
      overflow: hidden;
    }

    #cas {
      position: absolute;
      left:0;
      top:0;
      bottom:0;
      right:0;
      margin:auto;
      width:800px;
      height:600px;
      background-color: #70B7FD;
      z-index: 0;
    }
  </style>
</head>
<body>
<canvas id="cas"></canvas>

<script>
  !function() {
    var canvas = document.getElementById('cas');
    var ctx = canvas.getContext('2d');
    var rgb = '255';      // 线条颜色值
    var extendDis = 5;   // 可超出的画布边界
    var lineDis = 100;    // 连线距离

    lineDis *= lineDis;
    canvas.width = 800;
    canvas.height = 600;

    var RAF = (function() {
      return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function(callback) {
            window.setTimeout(callback, 1000 / 60);
          };
    })();

    // 鼠标活动时，获取鼠标坐标
    var warea = {x: null, y: null};

    window.onmousemove = function(e) {
      e = e || window.event;

      warea.x = e.clientX - canvas.offsetLeft;
      warea.y = e.clientY - canvas.offsetTop;
    };

    window.onmouseout = function(e) {
      warea.x = null;
      warea.y = null;
    };

    // 添加粒子
    // x，y为粒子坐标，xa, ya为粒子xy轴加速度，max为连线的最大距离
    var dots = [];
    for (var i = 0; i < 150; i++) {
      var x = Math.random() * (canvas.width + 2 * extendDis) - extendDis;
      var y = Math.random() * (canvas.height + 2 * extendDis) - extendDis;
      var xa = (Math.random() * 2 - 1) / 1.5;
      var ya = (Math.random() * 2 - 1) / 1.5;

      dots.push({
        x: x,
        y: y,
        xa: xa,
        ya: ya
      })
    }

    // 延迟100秒开始执行动画，如果立即执行有时位置计算会出错
    setTimeout(function() {
      animate();
    }, 100);

    // 每一帧循环的逻辑
    function animate() {
      ctx.clearRect(0, 0, canvas.width, canvas.height);

      bubDrawLine([warea].concat(dots));

      RAF(animate);
    }

    /**
     * 逐个对比连线
     * @param ndots
     */
    function bubDrawLine(ndots) {
      var ndot;

      dots.forEach(function(dot) {

        move(dot);

        // 循环比对粒子间的距离
        for (var i = 0; i < ndots.length; i++) {
          ndot = ndots[i];

          if (dot === ndot || ndot.x === null || ndot.y === null) continue;

          var xc = dot.x - ndot.x;
          var yc = dot.y - ndot.y;

          // 如果x轴距离或y轴距离大于max,则不计算粒子距离
          if (xc > ndot.max || yc > lineDis) continue;

          // 两个粒子之间的距离
          var dis = xc * xc + yc * yc;

          // 如果粒子距离超过max,则不做处理
          if (dis > lineDis) continue;

          // 距离比
          var ratio;

          // 如果是鼠标，则让粒子向鼠标的位置移动
          if (ndot === warea && dis < 20000) {
            dot.x -= xc * 0.01;
            dot.y -= yc * 0.01;
          }

          // 计算距离比
          ratio = (lineDis - dis) / lineDis;

          // 粒子间连线
          ctx.beginPath();
          ctx.lineWidth = ratio / 2;
          ctx.strokeStyle = 'rgba(' + rgb + ', ' + rgb + ', ' + rgb + ', 1';
          ctx.moveTo(dot.x, dot.y);
          ctx.lineTo(ndot.x, ndot.y);
          ctx.stroke();
        }

        // 将已经计算过的粒子从数组中删除
        ndots.splice(ndots.indexOf(dot), 1);
      });
    }

    /**
     * 粒子移动
     * @param dot
     */
    function move(dot) {
      dot.x += dot.xa;
      dot.y += dot.ya;

      // 遇到边界将加速度反向
      dot.xa *= (dot.x > (canvas.width + extendDis) || dot.x < -extendDis) ? -1 : 1;
      dot.ya *= (dot.y > (canvas.height + extendDis) || dot.y < -extendDis) ? -1 : 1;

      // 绘制点
      ctx.fillStyle = 'rgba(' + rgb + ', ' + rgb + ', ' + rgb + ', 1';
      ctx.fillRect(dot.x - 0.5, dot.y - 0.5, 1, 1);
    }
  }();
</script>
</body>
</html>